"""
Main file 
"""


################################################################################
# Module: INTEL
################################################################################


################################################################################
# Module: LEARNING
################################################################################


################################################################################
# Module: AGENTS
################################################################################

class AgentBase:
    NAME = "BOOM HEADSHOT!!!"
    def __init__(self, id, team, settings=None, field_rects=None, field_grid=None, nav_mesh=None, blob=None):
        """ Each agent is initialized at the beginning of each game.
            The first agent (id==0) can use this to set up global variables.
            Note that the properties pertaining to the game field might not be
            given for each game.
        """
        
        self.id = id
        self.team = team
        self.mesh = nav_mesh
        self.grid = field_grid
        self.settings = settings
        self.goal = None
        
        self.ammoTarget = False
        self.EnemyCPTarget = []
        
        # Read the binary blob, we're not using it though
        if blob is not None:
            print "Agent %s-%d received binary blob of %s" % (
                ('BLU' if team else 'RED'), id, type(pickle.loads(blob.read())))
            # Reset the file so other agents can read it.
            blob.seek(0) 
        
        # Recommended way to share variables between agents.
        if id == 0:
            self.all_agents = self.__class__.all_agents = []
        self.all_agents.append(self)
        # determines if the line between this tank, and the shootable target
        # has a friendly target in it, if so this is not a valid target!
    def validTargetNoFriendlyFire(self, obs, target):
        for x in obs.friends:
            if(line_intersects_circ(obs.loc,target,x[0:2],12)):
                #print self.id, "FRIEDNLY FIRE!", obs.friends, target
                return False #Can shoot, friendly fire!
            #print "Fire in the hole!"
            #print self.id, "Fire in the hole!", obs.friends, target
            return True #we can shoot!
    
    def observe(self, observation):
        """ Each agent is passed an observation using this function,
            before being asked for an action. You can store either
            the observation object or its properties to use them
            to determine your action. Note that the observation object
            is modified in place.
        """
        self.observation = observation
        self.selected = observation.selected
        
    def action(self):
        """ This function is called every step and should
            return a tuple in the form: (turn, speed, shoot)
        """
        obs = self.observation
        # Check if agent reached goal.
        if self.goal is not None and point_dist(self.goal, obs.loc) < self.settings.tilesize:
            self.goal = None
            self.ammoTarget = False
            del self.EnemyCPTarget[:]
            
        if self.EnemyCPTarget:#do not move to the Enemy CP if its not of the enemy anymore
            CurrentCPTarget = filter(lambda x: x[0] == self.EnemyCPTarget[0][0] and x[1] == self.EnemyCPTarget[0][1], obs.cps)
            if(CurrentCPTarget[0][2] == self.team):
                self.goal = None
                del self.EnemyCPTarget[:]

            
            
        # Walk to ammo
        ammopacks = filter(lambda x: x[2] == "Ammo", obs.objects)
        if ammopacks and obs.ammo == 0:
            del self.EnemyCPTarget[:]
            self.goal = ammopacks[0][0:2]
            self.ammoTarget = True
        elif self.ammoTarget == True:
            self.ammoTarget = False
            self.goal = None
            
        # Drive to where the user clicked
        if self.selected and self.observation.clicked:
            self.goal = self.observation.clicked
        
        # Walk to random CP
        if self.goal is None:
            #determine the amount of uncaptured control points
            EnemyCPS = []
            for x in range(len(obs.cps)): 
                if obs.cps[x][2] != self.team:
                    EnemyCPS.append(obs.cps[x])
                    
            if(len(EnemyCPS) == 0): #if no enemy control points, move to random one
                if(obs.ammo == 0):
                    self.goal = obs.cps[random.randint(0,len(obs.cps)-1)][0:2]
                else:#move to middle
                    self.goal = filter(lambda x: x[0] == 328, obs.cps)[0][0:2]
                    targetx = self.goal[0] + random.randint(-20,20)
                    targety = self.goal[1] + random.randint(-20,20)
                    if not line_intersects_grid(obs.loc, (targetx, targety), self.grid, self.settings.tilesize):
                        self.goal = (targetx, targety)
                    
            else: #else move to an enemy one
                self.goal = EnemyCPS[random.randint(0,len(EnemyCPS)-1)][0:2]
                self.EnemyCPTarget.append(self.goal)
        # Shoot enemies
        shoot = False
        combat = False
        if obs.foes and obs.ammo > 0 and not line_intersects_grid(obs.loc, obs.foes[0][0:2], self.grid, self.settings.tilesize):
            combat = True
            del self.EnemyCPTarget[:]
            self.ammoTarget = False
            if (point_dist(obs.foes[0][0:2], obs.loc) < self.settings.max_range):
                self.goal = obs.foes[0][0:2]
                shoot = True
            else:
                self.goal = obs.foes[0][0:2]
        # Compute path, angle and drive
        path = find_path(obs.loc, self.goal, self.mesh, self.grid, self.settings.tilesize)
        if path:
            dx = path[0][0]-obs.loc[0]
            dy = path[0][1]-obs.loc[1]
            turn = angle_fix(math.atan2(dy, dx)-obs.angle)
            if turn > self.settings.max_turn or turn < -self.settings.max_turn:
                shoot = False
            speed = (dx**2 + dy**2)**0.5
            if combat and not shoot and len(filter(lambda x: x[2] == self.team,obs.cps)) > 1:
                speed = 0;
        else:
            turn = 0
            speed = 0
        
        if(shoot):
            print self.goal
            shoot = self.validTargetNoFriendlyFire(obs, self.goal)
        return (turn,speed,shoot)
        
    def debug(self, surface):
        """ Allows the agents to draw on the game UI,
            Refer to the pygame reference to see how you can
            draw on a pygame.surface. The given surface is
            not cleared automatically. Additionally, this
            function will only be called when the renderer is
            active, and it will only be called for the active team.
        """
        #import pygame
        # First agent clears the screen
        if self.id == 0:
            surface.fill((0,0,0,0))
        # Selected agents draw their info
        if self.selected:
            if self.goal is not None:
                pygame.draw.line(surface,(0,0,0),self.observation.loc, self.goal)
        
    def finalize(self, interrupted=False):
        """ This function is called after the game ends, 
            either due to time/score limits, or due to an
            interrupt (CTRL+C) by the user. Use it to
            store any learned variables and write logs/reports.
        """
        pass
   
   

class Agent(AgentBase):
    NAME = "BOOM HEADSHOT!!!"
    pass
